﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX.Direct3D9;
using SampleFramework;
using SlimDX;
using System.Diagnostics;

namespace starpillage.Rendering.Terrain
{
    public class HeightmapMesh : IResource, IGameComponent
    {
        GraphicsDeviceManager deviceManager;
        VertexBuffer vb;
        IndexBuffer ib;
        Effect effect;
        int vx;
        int vy;
        Func<int, int, float> hmap;
        int _TriangleCount { get { return (vx - 1) * (vy - 1) * 2 + (vy - 1) * 4; } }
        Device _Device { get { return deviceManager.Direct3D9.Device; } }

        public HeightmapMesh(int vx, int vy, Func<int, int, float> hmap)
        {
            this.vx = vx;
            this.vy = vy;
            this.hmap = hmap;
        }

        public void Update(GameTime t)
        {
        }

        public void Draw()
        {
            _Device.Indices = ib;
            _Device.SetStreamSource(0, vb, 0, 5 * sizeof(float));
            _Device.VertexFormat = VertexFormat.Position | VertexFormat.Texture1;

            int passCount = effect.Begin();
            for (int pass = 0; pass < passCount; pass++)
            {
                effect.BeginPass(pass);
                _Device.DrawIndexedPrimitives(PrimitiveType.TriangleStrip, 0, 0, vx * vy, 0, _TriangleCount);
                effect.EndPass();
            }
            effect.End();
        }

        public void Initialize(GraphicsDeviceManager graphicsDeviceManager)
        {
            this.deviceManager = graphicsDeviceManager;
            try
            {
                this.effect = Effect.FromFile(deviceManager.Direct3D9.Device, @"Rendering\Terrain\Terrain.fx", ShaderFlags.None);
            }
            catch (Direct3D9Exception e)
            {
                Debug.WriteLine(e);
                foreach (System.Collections.DictionaryEntry entry in e.Data)
                {
                    Debug.WriteLine(entry.Key);
                    Debug.WriteLine(entry.Value);
                }
                throw;
            }
            
            this.effect.Technique = "Terrain";
        }

        public void LoadContent()
        {
            {
                int vbSize = vx * vy * 5 * sizeof(float);
                vb = new VertexBuffer(_Device, vbSize, Usage.None, VertexFormat.Position | VertexFormat.Texture1, Pool.Managed);
                DataStream s = vb.Lock(0, vbSize, LockFlags.None);
                for (int y = 0; y < vy; y++)
                {
                    for (int x = 0; x < vx; x++)
                    {
                        s.Write<float>((float)x);
                        s.Write<float>((float)hmap(x, y)); // y is up...
                        s.Write<float>((float)y);
                        s.Write<float>((float)x / vx);
                        s.Write<float>((float)y / vy);
                    }
                }
                vb.Unlock();
            }

            {
                int ibSize = (_TriangleCount + 2) * sizeof(ushort);
                ib = new IndexBuffer(_Device, ibSize, Usage.None, Pool.Managed, true);
                DataStream s = ib.Lock(0, ibSize, LockFlags.None);
                for (int y = 0; y < vy - 1; y++)
                {
                    for (int x = 0; x < vx; x++)
                    {
                        //  *--*--*--*--*--*
                        //  |\ |\ |\ |\ |\ |
                        //  | \| \| \| \| \|
                        //  *--*--*--*--*--*
                        //  |\ |\ |\ |\ |\ |
                        //  | \| \| \| \| \|
                        //  *--*--*--*--*--*
                        s.Write<ushort>((ushort)(x + y * vx));
                        s.Write<ushort>((ushort)(x + (y + 1) * vx));
                    }

                    // Slivers connecting the triangle strips
                    s.Write<ushort>((ushort)(vx - 1 + y * vx)); // last again
                    s.Write<ushort>((ushort)((y + 1) * vx)); // next
                }
                ib.Unlock();
            }
        }

        public void UnloadContent()
        {
            vb.Dispose();
            ib.Dispose();
        }

        public void Dispose()
        {
            effect.Dispose();
        }
    }
}
